/**
 * Created by user on 2016-07-19.
 */

import GMModel from '../database/models/gm';
import PlayerManager from '../player/player-manager';
import UserRecord from '../database/models/userRecord'
import {PlayerModel} from '../database/models/player'
import GMRecord from '../database/models/record'
import serverApi from './deps/server-api'


function fail(message) {
  return {success: false, message: message.toString()};
}

function success(res = {}) {
  const r = res;
  r.success = true;
  return r;
}


export async function rechargeAfter(recordRaw) {
  await recordKickbackForGM(recordRaw)
  await saveRecord(recordRaw)
}

async function recordKickbackForGM(recordRaw) {
  const {kickback, kickback2, currency} = recordRaw
  const cashRecharge = currency === 'cash'
  const goldRecharge = currency === 'gold'

  const needKickback = recordRaw.relation.length > 0 && kickback > 0 && !goldRecharge
  if (needKickback) {
    const [inviter, inviterSuperior] = recordRaw.relation
    const inviterUpdater = cashRecharge ?
      {$inc: {cashKickback: kickback}} : {$inc: {gemKickback: kickback}}
    const inviterSuperiorUpdater = cashRecharge ?
      {$inc: {cashKickback: kickback2}} : {$inc: {gemKickback: kickback2}}

    await GMModel.update({_id: inviter}, inviterUpdater)
    await GMModel.update({_id: inviterSuperior}, inviterSuperiorUpdater)
  }
}


async function saveRecord({
                            player, gm, relation, currency, amount,
                            source, kickback = 0, kickback2 = 0
                          }) {

  await UserRecord.create({
    from: gm._id,
    to: player._id,
    source,
    currency,
    relation,
    amount,
    kickback,
    kickback2
  })
}

const roles = ['super', 'level1', 'level2']
const genChildRole = (gm) => {
  const idx = roles.indexOf(gm.role)
  return roles[idx + 1]
}
const getRelationsFromGM = (gm) => {
  let relation = [gm]
  switch (gm.role) {
    case 'level2':
      relation.push(gm.superior)
      break
    case 'level3':
      relation.push(gm.superior, gm.superior.superior)
      break
  }
  return relation
}

const superApi = {
  'server/status': async () => {
    const {players, rooms} = await serverApi.getStatus()
    return success({players, rooms})
  },


  'delete-gm': async (data) => {
    const {username} = data;

    try {
      await GMModel.remove({username});
      return success();
    } catch (err) {
      return fail(err);
    }
  },
  'levelUp': async ({id}) => {
    try {
      await GMModel.update({_id: id}, {$set: {role: 'level1'}})
      return success();
    } catch (err) {
      return fail(err);
    }
  },
  'levelDown': async ({id}) => {
    try {
      await GMModel.update({_id: id}, {$set: {role: 'level2'}})
      return success()
    } catch (err) {
      return fail(err)
    }
  }
};

const normalApi = {
  'gm-list': async (_, curUser) => {
    try {
      const gms = await GMModel.find({superior: curUser});
      return success({gms});
    } catch (err) {
      return fail(err);
    }
  },

  'sub-gm-with-invited': async ({username}) => {
    try {
      const gm = await GMModel.findOne({username});
      const subGMs = await GMModel.find({superior: gm})
      const invited = await PlayerModel.find({inviteBy: gm._id})
      return success({gms: subGMs, invited})
    } catch (err) {
      return fail(err)
    }
  },

  'charge-gm': async ({id, amount}, curUser) => {
    try {
      const relation = getRelationsFromGM(curUser)
      const from = await GMModel.findOne({_id: curUser._id})
      if (curUser.role !== 'super' && from.gold < amount) {
        throw new Error('ACCOUNT_BALANCE_NOT_ENOUGH')
      }

      await GMModel.update({_id: id}, {$inc: {gold: amount}});
      if (curUser.role !== 'super') {
        await GMModel.update({_id: from._id}, {$inc: {gold: -1 * amount}})
      }
      await GMModel.update({_id: from._id}, {$inc: {spendGold: amount}})

      await new GMRecord({
        from,
        to: id,
        amount,
        relation
      })
        .save()
      return success({});
    } catch (err) {
      return fail(err);
    }
  },

  'charge-gm-gem': async ({id, amount}, curUser) => {
    try {

      let relation = getRelationsFromGM(curUser)
      const from = await GMModel.findOne({_id: curUser._id})
      if (curUser.role !== 'super' && from.gem < amount) {
        throw new Error('ACCOUNT_BALANCE_NOT_ENOUGH')
      }

      await GMModel.update({_id: id}, {$inc: {gem: amount}});
      if (curUser.role !== 'super') {
        await GMModel.update({_id: from._id}, {$inc: {gem: -1 * amount}})
      }
      await GMModel.update({_id: from._id}, {$inc: {spendGem: amount}})

      await new GMRecord({from, to: id, amount, relation}).save()
      return success({});
    } catch (err) {
      return fail(err);
    }
  },


  notice: async ({message}) => {
    const ok = await serverApi.notice(message)
    if (ok)
      return success({ok})
    return fail('fail')
  },

  'get-player-model': async ({shortId}) => {
    const model = await PlayerModel.findOne({shortId})
    if (!model) {
      return fail('玩家不存在。');
    }
    const _id = model._id;
    const player = PlayerManager.getInstance().getPlayer(_id);
    if (player) {
      return success({model: player.model});
    }
    return success({model});
  },

  freeze: async ({_id, days}) => {
    const player = PlayerManager.getInstance().getPlayer(_id);
    const freezeDate = new Date();
    freezeDate.setDate(freezeDate.getDate() + days || 0);
    if (player) {
      player.model.freezeDate = freezeDate;
      player.disconnect();
    }
    try {
      await PlayerModel.update({_id}, {
        $set: {freezeDate},
      });
      return success();
    } catch (err) {
      return fail(err.message);
    }
  },

  unfreeze: async ({_id}) => {
    try {
      await PlayerModel.update({_id}, {
        $unset: {freezeDate: ''},
      });
      return success();
    } catch (err) {
      return fail(err.message);
    }
  },

  'add-resource': async ({_id, gold, gem}, onlineGM) => {
    const gm = await GMModel.findOne({_id: onlineGM._id}).exec()

    let addGold = gold || 0;
    let addGem = gem || 0;
    if (typeof addGold !== 'number') {
      return fail('格式错误.');
    }

    if (typeof addGem !== 'number') {
      return fail('格式错误.');
    }

    if (gm.role !== 'super') {
      if (addGem < 0 || addGold < 0) {
        return fail('格式错误')
      }
    }

    addGold = Math.floor(addGold)
    addGem = Math.floor(addGem)

    if (gm.gold < addGold && gm.role !== 'super') {
      return fail('金额不足')
    }
    if (gm.gem < addGem && gm.role !== 'super') {
      return fail('房卡不足')
    }

    try {
      await serverApi.addResource(_id, addGem, addGold)
      const player = await PlayerModel.findOne({_id})
        .populate('inviteBy')
        .exec()

      if (!player) {
        return fail('not userId')
      }

      player.gem += addGem
      player.gold += addGold
      await player.save()

      gm.gem -= addGem
      gm.spendGem += addGem
      gm.gold -= addGold
      gm.spendGold += addGold
      await gm.save()

      const source = 'admin'
      const relation = player.inviteBy ? [player.inviteBy._id, player.inviteBy.superior] : []
      const currency = addGem ? 'gem' : 'gold'
      const amount = addGem || addGold

      await rechargeAfter({player, gm, relation, currency, amount, source})

      return success()
    } catch (err) {
      return fail(err.message);
    }
  },


  'add-gm': async (data, curUser) => {
    const {username, password} = data;
    if (!username) {
      return fail('需要账号。');
    }
    if (!password) {
      return fail('需要密码。');
    }

    if (curUser.role === 'level2') {
      return fail({info: 'level2 cannot create gm!'})
    }

    try {
      const exist = await GMModel.findOne({username})
      if (exist) {
        return fail('账号已存在。');
      }

      const pw = GMModel.generateHash(password)
      const gm = new GMModel({
        username,
        password: pw,
        role: genChildRole(curUser),
        superior: curUser,
        relation: curUser.relation.concat(curUser.id)
      });

      await gm.save();
      return success({gm});
    } catch (err) {
      return fail(err);
    }
  },
};

const accountApi = {
  info: (req, res) => {
    res.json({success: true, user: req.user})
  },

  records: async (req, res) => {
    try {
      const gm = req.user
      const isSuper = gm.role === 'super'
      const query = isSuper ? {} : {from: gm._id}
      const records = await UserRecord
        .find(query)
        .populate('from to relation')
        .sort({created: -1})
        .lean()
        .exec()
      res.json({success: true, records})
    } catch (err) {
      res.json({success: false, message: err.message})
    }
  },

  'gmRecords': async (req, res) => {
    let records = []
    try {
      const curUser = req.user
      if (curUser.role === 'super') {
        records = await GMRecord.find().populate('from to').sort({created: -1}).lean()
      } else {
        records = await GMRecord.find({from: curUser._id}).populate('from to').sort({created: -1}).lean()
      }
      res.json({success: true, records})
    } catch (err) {
      res.json({success: false, message: err.message})
    }
  }
}

export {superApi, normalApi, accountApi};
